/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.cef.generator.repository.common;

import com.inspur.edp.cef.core.rootmanager.cefReadWriteLock;
import com.inspur.edp.cef.entity.config.CefConfig;
import com.inspur.edp.cef.entity.config.CefExtendConfig;
import com.inspur.edp.cef.entity.config.MgrConfig;
import com.inspur.edp.cef.gencommon.config.CefConfigManager;
import com.inspur.edp.cef.gencommon.config.CefConfigUtils;
import com.inspur.edp.cef.generator.overall.CefGenerator;

import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public abstract class CefTypeManager {
    private cefReadWriteLock locker = new cefReadWriteLock();
    //protected readonly IDictionary<string, CefGenerateContext> cache = new Dictionary<string, CefGenerateContext>();
    protected final java.util.Map<String, GeneratedContext> cache = new java.util.HashMap<String, GeneratedContext>();

    public final GeneratedContext getCefType(String configId) {
        GeneratedContext rez = null;

        if ((rez = cache.get(configId)) != null) {
            return rez;
        }
        //Java版临时替换：暂时去掉锁。
//		locker.AcquireReaderLock(-1);
        try {
            if ((rez = cache.get(configId)) != null) {
                return rez;
            }
            //Java版临时替换：暂时去掉锁。
//			var lockCookie = locker.UpgradeToWriterLock(-1);
            try {
                if ((rez = cache.get(configId)) != null) {
                    return rez;
                }
                rez = getTypeFromLocal(configId);
                if (rez != null) {
                    cache.put(configId, rez);
                    return rez;
                }
                return getNullByConfigId(configId);

//				//ServiceManager.getService<GSPLogService>().GetLogger("GSPBefLogger").Log("重新编译"+ configId, LogLevel.Info);
//				CefGenerateContext cefContext = innerCreateCefType(configId);
//				GeneratedContext tempVar = new GeneratedContext();
//				tempVar.setCurrentTypeInfo(cefContext.getCurrentTypeInfo());
//				//Java版临时注释：不需要Assembly了。
////				tempVar.setGeneratedAssembly(cefContext.GeneratedAssembly);
//				tempVar.setRepositoryType(cefContext.getRepositoryType());
//				tempVar.setCefConfig(cefContext.getCefConfig());
//				tempVar.setManagerImplType(cefContext.getManagerImplType());
//				cache.put(configId, rez = tempVar);
//				//cache[configId] = rez = cefContext;
//				return rez;
            } finally {
                //Java版临时替换：暂时去掉锁。
//				RefObject<var> tempRef_lockCookie = new RefObject<var>(lockCookie);
//				locker.DowngradeFromWriterLock(tempRef_lockCookie);
//				lockCookie = tempRef_lockCookie.argvalue;
            }
        } finally {
            //Java版临时替换：暂时去掉锁。
//			locker.ReleaseReaderLock();
        }
    }

    protected  GeneratedContext getNullByConfigId(String configId)
    {
        return null;
//        throw new RuntimeException("CefTypeManager.getCefType无法加载程序集, configId:" + configId);
    }

    private GeneratedContext getTypeFromLocal(String configId) {
        CefConfig config = getCefConfig(configId);
        if(config==null)
            return null;
        CefMgrTypeInfo typeInfo = getTypeInfo(config.getMgrConfig());
        return buildGeneratedContext(config, configId, typeInfo);
    }

//
//	private String getMd5(FileStream file) {
//		//Java版暂时不从内存中获取，不做MD5码比较。
////		//FileStream file = new FileStream(fileName, System.IO.FileMode.Open);
////		MD5 md5 = new MD5CryptoServiceProvider();
////		byte[] retVal = md5.ComputeHash(file);
////		file.Close();
////		StringBuilder sb = new StringBuilder();
////		for (int i = 0; i < retVal.length; i++)
////		{
////			sb.append(String.format("%02x", retVal[i]));
////		}
////		return sb.toString();
//	}

    private CefMgrTypeInfo getTypeInfo(MgrConfig config) {
        CefMgrTypeInfo typeInfo = new CefMgrTypeInfo();
//        typeInfo.setImplType(CefConfigUtils.getType(config.getImpAssembly(), config.getImpClass()));
//        typeInfo.setInterfaceType(CefConfigUtils.getType(config.getAssembly(), config.getClassName()));
//        typeInfo.setAllInterfaceType(CefConfigUtils.getType(config.getAllInterfaceAssembly(), config.getAllInterfaceClassName()));
        return typeInfo;
    }

    private void initMgrTypeInfo(CefMgrTypeInfo typeInfo, MgrConfig config){
        typeInfo.setImplType(CefConfigUtils.getType(config.getImpAssembly(), config.getImpClass()));
        typeInfo.setInterfaceType(CefConfigUtils.getType(config.getAssembly(), config.getClassName()));
        typeInfo.setAllInterfaceType(CefConfigUtils.getType(config.getAllInterfaceAssembly(), config.getAllInterfaceClassName()));

    }
    /**
     * 子类实现
     *
     * @param config
     * @param configId
     * @param typeInfo
     * @return
     */
    protected GeneratedContext buildGeneratedContext(CefConfig config, String configId, CefMgrTypeInfo typeInfo) {
        return null;
    }

    public final CefGenerator preCompile(String configId, CefConfig cefConfig,
                                         ArrayList<CefExtendConfig> extendConfigs,
                                         String generatedPath,
                                         String serverPath, CompileContext compileContext) {

//		if (!isNeedCompile(cefConfig, generatedPath)) {
//			return null;
//		}
        CefMgrTypeInfo parentTypeInfo = getPreRefType(cefConfig);
        cefConfig.setExtendConfigs(extendConfigs);
        ArrayList<MgrConfig> mgrConfigs = CefConfigUtils.getMgrConfigListByLayer(cefConfig, extendConfigs);
        CefGenerator gen = getCefGenerator(mgrConfigs, parentTypeInfo, cefConfig, generatedPath, serverPath,compileContext);
        return gen;
//		gen.initialize();
//		gen.generate();
//		return gen.getCefGenContext();
    }

    private boolean isNeedCompile(CefConfig cefConfig, String generatedPath) {
        return false;
//Java版预编译：目前只支持设计时编译，不支持运行时重新编译。

//		var assemblyName = cefConfig.getDefaultNamespace() + "." + cefConfig.getID().getName() + ".dll";

//		var fileName = Path.Combine(generatedPath, assemblyName);
//		if (!File.Exists(fileName))
//		{
//			return true;
//		}
//
//		Assembly assembly = LoadAssembly(fileName);
//		if (assembly == null)
//		{
//			return true;
//		}

//		var attributes = assembly.<CefVersionAttribute>getCustomAttributes();

//		var version = attributes.First().Version;
//

//		var apiAssembly = Assembly.Load(cefConfig.getMgrConfig().getAssembly());
//		//var apiTime = Convert.ToString(File.getLastWriteTime(apiAssembly.Location));

//		var apiFileName = apiAssembly.getName().getName() + ".dll";

//		var file = apiAssembly.getFile(apiFileName);
//		String apiMd5 = getMd5(file);
//
//		if (apiMd5.equals(version))
//		{
//			return false;
//		}
//
//		return true;
    }

    //	private Assembly loadAssembly(String path)
//	{
//		try
//		{

////			using (FileStream fsRead = new FileStream(path, FileMode.Open, FileAccess.Read))
//			FileStream fsRead = new FileStream(path, FileMode.Open, FileAccess.Read);
//			try
//			{
//				long fsLen = fsRead.getLength();
//				byte[] fsByte = new byte[fsLen];
//				fsRead.Read(fsByte, 0, fsByte.length);
//				return Assembly.Load(fsByte);
//			}
//			finally
//			{
//				fsRead.dispose();
//			}
//		}
//		catch (java.lang.Exception e)
//		{
//			return null;
//		}
//
//	}
//    private CefGenerateContext innerCreateCefType(String configId) {
//        CefConfigManager configMgr = getConfigManager();
//        CefConfig cefConfig = getCefConfig(configId);
//        CefMgrTypeInfo parentTypeInfo = getRefType(cefConfig);
//        ArrayList<CefExtendConfig> extendConfigs = configMgr.getExtendConfigs(cefConfig);
//        cefConfig.setExtendConfigs(extendConfigs);
//        ArrayList<MgrConfig> mgrConfigs = CefConfigUtils.getMgrConfigListByLayer(cefConfig, extendConfigs);
//        CefGenerator gen = getCefGenerator(mgrConfigs, parentTypeInfo, cefConfig);
//        gen.initialize();
//        gen.generate();
//
//        return gen.getCefGenContext();
//    }

    private CefConfig getCefConfig(String configId) {
        CefConfigManager configMgr = getConfigManager();
        CefConfig cefConfig = configMgr.getCefConfigById(configId);
//        if (cefConfig == null) {
//            throw new RuntimeException("没有找到[" + configId + "]的相关配置，请检查元数据是否部署，或SU路径下是否存在[ServiceUnit.json]文件");
//        }
        return cefConfig;
    }

    private CefConfig getCefConfigByMDId(String mdId) {
        CefConfigManager configMgr = getConfigManager();
        CefConfig cefConfig = configMgr.getCefConfigByMDId(mdId);
//        if (cefConfig == null) {
//            throw new RuntimeException("没有找到元数据Id为[" + mdId + "]的相关配置，请检查元数据是否部署，或SU路径下是否存在[ServiceUnit.json]文件");
//        }
        return cefConfig;
    }

    private CefMgrTypeInfo getRefType(CefConfig cefConfig) {
        if (cefConfig.getSourceID() == null || cefConfig.getSourceID().isEmpty()) {
            return getBasicMgrTypeInfo();
        }
        return getCefType(cefConfig.getSourceID()).getCurrentTypeInfo();
    }

    private CefMgrTypeInfo getPreRefType(CefConfig cefConfig) {
        if (cefConfig.getSourceID() == null || cefConfig.getSourceID().isEmpty()) {
            return getBasicMgrTypeInfo();
        }
        GeneratedContext context = getCefType(cefConfig.getSourceID());
        initMgrTypeInfo(context.getCurrentTypeInfo(), context.getCefConfig().getMgrConfig());
        return context.getCurrentTypeInfo();
    }

    protected abstract CefMgrTypeInfo getBasicMgrTypeInfo();

    protected abstract CefConfigManager getConfigManager();

    protected abstract CefGenerator getCefGenerator(ArrayList<MgrConfig> mgrConfigs, CefMgrTypeInfo parentTypeInfo, CefConfig cefConfig);

    protected CefGenerator getCefGenerator(ArrayList<MgrConfig> mgrConfigs, CefMgrTypeInfo parentTypeInfo, CefConfig cefConfig, String generatedPath, String serverPath, CompileContext compileContext) {
        return null;
    }

    //region ByMDId
    private ReentrantReadWriteLock mdlock = new ReentrantReadWriteLock();
    protected final java.util.Map<String, GeneratedContext> mdCache = new java.util.HashMap<String, GeneratedContext>();

    public final GeneratedContext getCefTypeByMDId(String mdId) {
        GeneratedContext rez = null;

        if ((rez = mdCache.get(mdId)) != null) {
            return rez;
        }
        mdlock.readLock().lock();
        try {
            if ((rez = mdCache.get(mdId)) != null) {
                return rez;
            }
        } finally {
            mdlock.readLock().unlock();
        }
        mdlock.writeLock().lock();
        try {
            if ((rez = mdCache.get(mdId)) != null) {
                return rez;
            }
            rez = getTypeFromLocalByMDId(mdId);
            if (rez != null) {
                mdCache.put(mdId, rez);
                return rez;
            }
            return null;
//            throw new RuntimeException("CefTypeManager.getCefType无法加载程序集, configId:" + mdId);
        } finally {
            mdlock.writeLock().unlock();
        }
    }

    private GeneratedContext getTypeFromLocalByMDId(String mdId) {
        CefConfig config = getCefConfigByMDId(mdId);
        if(config == null)
            return null;
        CefMgrTypeInfo typeInfo = getTypeInfo(config.getMgrConfig());
        return buildGeneratedContext(config, config.getID(), typeInfo);
    }
    //endregion
}
